package com.szcinda.service.dto.log;

import com.szcinda.repository.*;
import com.szcinda.service.DateUtils;
import com.szcinda.service.PageResult;
import com.szcinda.service.SnowFlakeFactory;
import com.szcinda.service.dto.organization.OrganizationService;
import com.szcinda.service.dto.role.RoleRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class LogServiceImpl implements LogService {

    private final OperationLogRepository logRepository;
    private final SnowFlakeFactory snowFlakeFactory;
    private final UserRepository userRepository;
    private OrganizationService organizationService;
    private final RoleRepository roleRepository;

    public LogServiceImpl(OperationLogRepository logRepository, UserRepository userRepository, OrganizationService organizationService, RoleRepository roleRepository) {
        this.logRepository = logRepository;
        this.userRepository = userRepository;
        this.organizationService = organizationService;
        this.roleRepository = roleRepository;
        this.snowFlakeFactory = SnowFlakeFactory.getInstance();
    }

    @Override
    public void create(LogCreateDto createDto) {
        OperationLog log = new OperationLog();
        BeanUtils.copyProperties(createDto, log);
        log.setId(snowFlakeFactory.nextId("LG"));
        logRepository.save(log);
    }

    @Override
    public PageResult<OperationLog> query(LogQueryDto params) {
        User user = userRepository.findById(params.getUserId());
        // 如果是超级管理员，则能查到所有的用户日志，如果是机构管理员，能看到当前机构和子机构的用户日志，普通用户只能看到自己的日志
        Role role = roleRepository.findById(user.getRoleId());
        List<User> users = null;
        if (role.getName().equals(RoleType.SUPER_ADMIN)) {
            users = userRepository.findAll();
        } else if ("机构管理员".equals(role.getName()) || RoleType.ADMIN.equals(role.getName())) {
            List<String> childOrgIds = organizationService.getChildOrgIds(user.getOrganizationId());
            users = userRepository.findByOrganizationIdIn(childOrgIds);
        } else if (role.isEqualAdmin()) {
            // 如果授予了机构管理员权限
            List<String> childOrgIds = organizationService.getChildOrgIds(user.getOrganizationId());
            users = userRepository.findByOrganizationIdIn(childOrgIds);
        } else {
            users = Collections.singletonList(user);
        }
        List<String> nameList = users.stream().map(User::getNickName).collect(Collectors.toList());
        Specification<OperationLog> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("userName");
            predicates.add(exp.in(nameList));
            if (!StringUtils.isEmpty(params.getUserName())) {
                Predicate site = criteriaBuilder.like(root.get("userName").as(String.class), params.getUserName().trim() + "%");
                predicates.add(site);
            }
            if (!StringUtils.isEmpty(params.getTargetId())) {
                Predicate site = criteriaBuilder.like(root.get("targetId").as(String.class), params.getTargetId().trim() + "%");
                predicates.add(site);
            }
            if (params.getCreateTimeStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), params.getCreateTimeStart().atStartOfDay());
                predicates.add(timeStart);
            }
            if (params.getCreateTimeEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThanOrEqualTo(root.get("createTime"), params.getCreateTimeEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(params.getPage() - 1, params.getPageSize(), sort);
        Page<OperationLog> details = logRepository.findAll(specification, pageable);
        return PageResult.of(details.getContent(), params.getPage(), params.getPageSize(), details.getTotalElements());
    }

    @Override
    public List<OperationLogDto> getByUserId(String userId, String orgId) {
        // 过滤当前机构相关的日志
        User user = userRepository.findById(userId);
        // 如果是超级管理员，则能查到所有的用户日志，如果是机构管理员，能看到当前机构和子机构的用户日志，普通用户只能看到自己的日志
        Role role = roleRepository.findById(user.getRoleId());
        List<User> users;
        if (role.getName().equals(RoleType.SUPER_ADMIN)) {
            users = userRepository.findAll();
        } else if (RoleType.ADMIN2.equals(role.getName()) || RoleType.ADMIN.equals(role.getName())) {
            List<String> childOrgIds = organizationService.getChildOrgIds(user.getOrganizationId());
            users = userRepository.findByOrganizationIdIn(childOrgIds);
        } else if (role.isEqualAdmin()) {
            // 如果授予了机构管理员权限
            List<String> childOrgIds = organizationService.getChildOrgIds(user.getOrganizationId());
            users = userRepository.findByOrganizationIdIn(childOrgIds);
        } else {
            users = Collections.singletonList(user);
        }
        List<String> nameList = users.stream().map(User::getNickName).collect(Collectors.toList());
        Specification<OperationLog> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("userName");
            predicates.add(exp.in(nameList));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(0, 100, sort);
        List<OperationLog> content = logRepository.findAll(specification, pageable).getContent();
        List<OperationLogDto> dtos = new ArrayList<>();
        if (content != null && content.size() > 0) {
            for (OperationLog operationLog : content) {
                OperationLogDto dto = new OperationLogDto();
                BeanUtils.copyProperties(operationLog, dto);
                dto.setDate(operationLog.getCreateTime().toLocalDate().format(DateUtils.df_cn));
                dto.setTime(operationLog.getCreateTime().format(DateUtils.df_time));
                dtos.add(dto);
            }
        }
        return dtos;
    }
}
